master
  1---
  2import { siteConfig } from '@/config';
  3import type { BlogPostData } from '@/types/data';
  4import ProfileCard from '@components/aside/ProfileCard.astro';
  5import TOC from '@components/aside/TOC.astro';
  6import MetaList from '@components/widgets/MetaList.astro';
  7import MainLayout from '@layouts/MainLayout.astro';
  8import { getCategoryUrl, getPostsCount, getSortedPosts, getTagUrl } from '@utils/content-utils';
  9import { t } from '@utils/i18n';
 10import { Icon } from 'astro-icon/components';
 11import dayjs from 'dayjs';
 12
 13export async function getStaticPaths() {
 14  const allBlogPosts = await getSortedPosts();
 15  const yearMap = new Map<number, Map<number, { body: string; data: BlogPostData }[]>>();
 16  for (const post of allBlogPosts) {
 17    const time = dayjs(post.data.published);
 18    const year = time.year();
 19    const month = time.month() + 1;
 20    let monthMap = yearMap.get(year);
 21    if (!monthMap) {
 22      monthMap = new Map();
 23      yearMap.set(year, monthMap);
 24    }
 25    let monthPosts = monthMap.get(month);
 26    if (!monthPosts) {
 27      monthPosts = [];
 28      monthMap.set(month, monthPosts);
 29    }
 30    monthPosts.push(post);
 31  }
 32  const data = Array.from(yearMap.entries()).map(([year, monthMap]) => ({
 33    year,
 34    data: Array.from(monthMap.entries()).map(([month, postData]) => ({
 35      month,
 36      data: postData,
 37    })),
 38  }));
 39  const paths = [
 40    {
 41      params: {
 42        time: undefined,
 43      },
 44      props: {
 45        data,
 46      },
 47    },
 48    ...data.map(({ year }) => ({
 49      params: {
 50        time: year,
 51      },
 52      props: {
 53        data: data,
 54      },
 55    })),
 56  ];
 57  return paths;
 58}
 59
 60const { data } = Astro.props;
 61const currentYear = Number(Astro.params.time);
 62const postCount = await getPostsCount();
 63---
 64
 65<MainLayout>
 66  <div class="card border-base-300 bg-base-200 swup-transition-fade border px-6 py-4">
 67    <div class="tooltip md:tooltip-right tooltip-bottom mx-auto w-fit">
 68      <h1 class="text-center text-3xl font-bold">{t.navigation.archive.title()}</h1>
 69      <div class="tooltip-content">
 70        {t.status.postsCount(postCount)}
 71      </div>
 72    </div>
 73    {
 74      (() => {
 75        function renderMonth(year: number, month: number) {
 76          let monthData = data
 77            .find((d) => d.year === year)
 78            ?.data.find((d) => d.month === month)?.data;
 79          if (!monthData) {
 80            console.log('[WARNING] Month data not found for year:', year, 'month:', month);
 81            return <p>SHOULD NOT RENDER THIS, IS A BUG</p>;
 82          }
 83          return (
 84            <ul class="list">
 85              {monthData.map(({ data }) => (
 86                <li class="list-row">
 87                  <div class="list-col-grow">
 88                    <a
 89                      href={`/posts/${data.slug}`}
 90                      title={data.title}
 91                      class="text-lg font-bold"
 92                    >
 93                      {data.title}
 94                    </a>
 95                    <MetaList
 96                      class="text-base-content/60 mt-2 items-start text-sm"
 97                      metas={[
 98                        {
 99                          icon: 'material-symbols:category-outline-rounded',
100                          text: data.category || t.meta.unCategorized(),
101                          link: getCategoryUrl(data.category),
102                        },
103                        {
104                          icon: 'material-symbols:tag-rounded',
105                          title: t.meta.tags(),
106                          group: data.tags.map((tag) => ({
107                            icon: 'material-symbols:tag-rounded',
108                            text: tag,
109                            link: getTagUrl(tag),
110                          })),
111                        },
112                      ]}
113                    />
114                  </div>
115                  <time
116                    datetime={data.published.toISOString()}
117                    data-no-relative="true"
118                    class="text-base-content/60"
119                  >
120                    {data.published.toLocaleDateString(siteConfig.lang.replace('_', '-'))}
121                  </time>
122                </li>
123              ))}
124            </ul>
125          );
126        }
127
128        function renderYear(year: number, reverse: boolean = false) {
129          const yearData = data.find((d) => d.year === year)?.data;
130          if (!yearData) {
131            console.log('[WARNING] Year data not found for year:', year);
132            return <p>SHOULD NOT RENDER THIS, IS A BUG</p>;
133          }
134          return (
135            <ul class="timeline timeline-snap-icon timeline-vertical max-md:timeline-compact w-full">
136              {yearData.map(({ month }, index) => (
137                <li>
138                  {index > 0 && <hr />}
139                  <div class="timeline-middle">
140                    <Icon
141                      name="material-symbols:add-circle-rounded"
142                      height="1.25rem"
143                      width="1.25rem"
144                    />
145                  </div>
146                  <div
147                    class:list={[
148                      `timeline-${(index % 2 === 0) !== reverse ? 'start' : 'end'}`,
149                      'w-full',
150                    ]}
151                  >
152                    <div
153                      class:list={[
154                        (index % 2 === 0) !== reverse && 'md:text-end',
155                        'mx-4 text-2xl font-bold',
156                      ]}
157                      id={`${year}-${month}`}
158                    >
159                      {month}
160                    </div>
161                    <div class="mx-2">{renderMonth(year, month)}</div>
162                  </div>
163                  <hr />
164                </li>
165              ))}
166            </ul>
167          );
168        }
169
170        function renderAll() {
171          let totalMonths = 0;
172          return data.map(({ year, data: yearData }) => {
173            const reverse = totalMonths % 2 !== 0;
174            totalMonths += yearData.length;
175            return (
176              <>
177                <div class="divider mt-12 scroll-mt-20 text-2xl font-bold" id={`${year}`}>
178                  <a
179                    href={`/archives/${year}`}
180                    title={`${year}`}
181                    class="hover:text-primary duration-200"
182                  >
183                    {year}
184                  </a>
185                </div>
186                <div class="px-4">{renderYear(year, reverse)}</div>
187              </>
188            );
189          });
190        }
191
192        if (currentYear) {
193          return renderYear(currentYear);
194        } else {
195          return renderAll();
196        }
197      })()
198    }
199  </div>
200  <Fragment slot="aside-fixed">
201    <ProfileCard />
202  </Fragment>
203  <Fragment slot="aside-sticky">
204    {
205      currentYear ? (
206        (() => {
207          const yearData = data.find((d) => d.year === currentYear);
208
209          return (
210            yearData && (
211              <TOC
212                headings={yearData.data.map(({ month }) => ({
213                  depth: 2,
214                  text: month.toString(),
215                  slug: `${currentYear}-${month}`,
216                }))}
217              />
218            )
219          );
220        })()
221      ) : (
222        <TOC
223          headings={data.flatMap(({ year, data }) => [
224            {
225              depth: 2,
226              text: year.toString(),
227              slug: `${year}`,
228            },
229            ...data.map(({ month }) => ({
230              depth: 3,
231              text: month.toString(),
232              slug: `${year}-${month}`,
233            })),
234          ])}
235        />
236      )
237    }
238  </Fragment>
239</MainLayout>